import minimalmodbus
import serial
import time

class LE100485Sensor:
    def __init__(self, port='COM3', baudrate=9600, sensor_address=1, range_type=50):
        """
        初始化传感器
        :param port: 串口端口（Windows：COMx，Linux：/dev/ttyUSBx）
        :param baudrate: 波特率（默认9600bps，可根据传感器配置调整）
        :param sensor_address: 传感器从机地址（默认1，范围1-247）
        :param range_type: 传感器量程类型（50=50量程，100=100量程）
        """
        # 初始化Modbus-RTU通讯
        self.instrument = minimalmodbus.Instrument(port, sensor_address)
        self.instrument.serial.baudrate = baudrate
        self.instrument.serial.bytesize = serial.EIGHTBITS  # 数据位8
        self.instrument.serial.parity = serial.PARITY_NONE  # 无校验
        self.instrument.serial.stopbits = serial.STOPBITS_ONE  # 停止位1
        self.instrument.serial.timeout = 1  # 读取超时时间（秒）
        self.instrument.mode = minimalmodbus.MODE_RTU  # RTU模式
        self.instrument.clear_buffers_before_each_transaction = True  # 每次交易前清空缓冲区

        # 传感器参数（根据量程类型配置，关键修正）
        self.distance_register = 0x003B  # 距离值起始寄存器地址
        self.range_type = range_type
        
        if range_type == 50:
            self.number_of_registers = 1  # 50量程：1个寄存器（16位）
            self.resolution = 0.001       # 50量程分辨率：0.001mm（1μm）- 保持不变（读取正确）
        elif range_type == 100:
            self.number_of_registers = 2  # 100量程：2个寄存器（32位）
            self.resolution = 0.001      # 关键修正1：100量程分辨率改为0.0001mm（0.1μm）
        else:
            raise ValueError("量程类型仅支持50或100")

    def read_distance(self, register_address=None, number_of_registers=None):
        """
        读取传感器距离测量数据
        :param register_address: 距离数据起始寄存器地址（默认使用初始化配置）
        :param number_of_registers: 读取寄存器个数（默认使用量程对应的配置）
        :return: 实际距离值（单位：mm），读取失败返回None
        """
        # 使用默认配置（根据量程类型）
        if register_address is None:
            register_address = self.distance_register
        if number_of_registers is None:
            number_of_registers = self.number_of_registers

        try:
            if self.range_type == 50:
                # 50量程：读取1个16位无符号寄存器（保持原有逻辑，读取正确）
                raw_data = self.instrument.read_register(
                    registeraddress=register_address,
                    number_of_decimals=0,
                    functioncode=3,
                    signed=False
                )
                actual_distance = raw_data * self.resolution

            elif self.range_type == 100:
                # 100量程：读取2个16位寄存器（小端模式）
                raw_data_list = self.instrument.read_registers(
                    registeraddress=register_address,
                    number_of_registers=number_of_registers,
                    functioncode=3
                )
                
                # 关键修正2：小端模式组合32位数据（低16位 << 16 + 高16位）
                # 示例：如果返回数据是 [0x2A85, 0x0100]（假设），组合后为 0x01002A85 = 16780933
                # 16780933 × 0.0001mm = 1678.0933mm（不对，再结合你的实际值反推）
                # 你的实际值99.63mm → 99.63 / 0.0001 = 996300（需要raw_data_32=996300）
                raw_data_32 = (raw_data_list[1] << 16) | raw_data_list[0]  # 小端模式组合
                actual_distance = raw_data_32 * self.resolution

                # 可选：限制量程范围（100量程应在0-100mm之间，过滤异常值）
                if not (0 <= actual_distance <= 100):
                    print(f"警告：距离值超出100量程范围：{actual_distance:.4f}mm")
                    return None

            return round(actual_distance, 4)  # 保留4位小数，便于观察

        except Exception as e:
            print(f"数据读取失败：{str(e)}")
            return None

    def continuous_read(self, interval=0.5):
        """
        连续读取距离数据
        :param interval: 读取间隔（秒），默认0.5秒一次
        """
        print(f"开始连续读取（{self.range_type}量程，间隔{interval}秒），按Ctrl+C停止...")
        try:
            while True:
                distance = self.read_distance()
                if distance is not None:
                    print(f"当前距离：{distance:.4f} mm")
                else:
                    print("读取失败，重试中...")
                time.sleep(interval)
        except KeyboardInterrupt:
            print("\n连续读取已停止")

    def close(self):
        """关闭串口连接"""
        self.instrument.serial.close()
        print("串口已关闭")

# 测试代码
if __name__ == "__main__":
    # ----------------------
    # 配置传感器（根据实际情况修改）
    # ----------------------
    # 100量程传感器（地址4，波特率115200，COM8）- 你的实际配置
    sensor = LE100485Sensor(
        port='COM8',
        baudrate=115200,
        sensor_address=4,
        range_type=100  # 指定100量程
    )

    # 50量程传感器示例（读取正确，保留）
    # sensor = LE100485Sensor(
    #     port='COM3',
    #     baudrate=9600,
    #     sensor_address=1,
    #     range_type=50
    # )

    try:
        # 方式1：单次读取
        single_distance = sensor.read_distance()
        if single_distance is not None:
            print(f"单次读取距离：{single_distance:.4f} mm")
        time.sleep(1)

        # 方式2：连续读取（推荐使用）
        sensor.continuous_read(interval=0.5)
    except Exception as e:
        print(f"程序异常：{str(e)}")
    finally:
        sensor.close()